//-----------------------------------------------------------------------------
// Commander Map HUD
//
// Portions Copyright (c) GarageGames.Com
// Copyright (c) Ben Garney
//-----------------------------------------------------------------------------

// These includes are probably overkill -- BJG
#include "platform/platform.h"
#include "platform/platformVideo.h"
#include "platform/platformAudio.h"
#include "platform/platformInput.h"
#include "core/findMatch.h"

#include "dgl/dgl.h"
#include "game/game.h"
#include "math/mMath.h"
#include "console/simBase.h"
#include "console/console.h"
#include "terrain/terrData.h"
#include "terrain/terrRender.h"
#include "terrain/waterBlock.h"
#include "game/collisionTest.h"
#include "game/showTSShape.h"
#include "sceneGraph/sceneGraph.h"
#include "gui/guiTSControl.h"
#include "game/moveManager.h"
#include "console/consoleTypes.h"
#include "game/shapeBase.h"
#include "core/dnet.h"
#include "game/gameConnection.h"
#include "core/fileStream.h"
#include "gui/guiCanvas.h"
#include "dgl/gTexManager.h"
#include "sceneGraph/sceneLighting.h"
#include "terrain/sky.h"
#include "game/ambientAudioManager.h"
#include "sim/frameAllocator.h"
#include "sceneGraph/detailManager.h"
#include "gui/guiMLTextCtrl.h"
#include "platform/profiler.h"
#include "game/fx/underLava.h"

//-----------------------------------------------------------------------------

class GuiCommanderHud : public GuiTSCtrl
{
private:
   typedef GuiTSCtrl Parent;


   Point2F mPanSpeed;
   F32     mZoomSpeed;
   S32     mLastRenderTime;

public:
   Point2F mPanGoal, mCurPan;
   F32     mZoomGoal, mCurZoom;

   GuiCommanderHud();

   bool processCameraQuery(CameraQuery *query);
   void renderWorld(const RectI &updateRect);

   void onRender( Point2I, const RectI &);
   static void initPersistFields();

   DECLARE_CONOBJECT( GuiCommanderHud );
};


//-----------------------------------------------------------------------------

IMPLEMENT_CONOBJECT( GuiCommanderHud );

GuiCommanderHud::GuiCommanderHud()
:   mPanSpeed(10, 10), mZoomSpeed(1), mCurPan(0,0), mCurZoom(M_PI_F/2),
    mPanGoal(0,0), mZoomGoal(M_PI_F/2), mLastRenderTime(0)
{
}

void GuiCommanderHud::initPersistFields()
{
   Parent::initPersistFields();

   addField("panSpeed",  TypePoint2F, Offset(mPanSpeed,  GuiCommanderHud), "Set the speed (x/y) we pan to our goal.");
   addField("zoomSpeed", TypeF32,     Offset(mZoomSpeed, GuiCommanderHud), "Set the speed we zoom with to our goal.");
}

bool GuiCommanderHud::processCameraQuery(CameraQuery *q)
{
   // Scale ranges based on the highest/lowest point in the terrain
   F32 maxHi = gClientSceneGraph->getCurrentTerrain()->findSquare(8, 0,0)->maxHeight / 10;
   F32 minHi = gClientSceneGraph->getCurrentTerrain()->findSquare(8, 0,0)->minHeight / 10;

   q->object = NULL;
   q->nearPlane = 1;
   q->farPlane  = mFabs(maxHi) + mFabs(minHi);
   q->fov       = mCurZoom;

   // Make us high up, facing straight down.
   q->cameraMatrix = MatrixF(EulerF(3.14/2, 0, 0)); // rotate us to look straight down
   q->cameraMatrix.setPosition(Point3F(mCurPan.x,mCurPan.y, maxHi + 100)); // and high enough we won't clip

   return true;
}

void GuiCommanderHud::renderWorld(const RectI &updateRect)
{
   // Set up state
   TerrainRender::mRenderingCommander = true;
   F32 oldVisDist = gClientSceneGraph->getVisibleDistance();
   gClientSceneGraph->setVisibleDistance(2000);
   F32 oldFogDist = gClientSceneGraph->getFogDistance();
   gClientSceneGraph->setFogDistance(2000);

   // set up the camera and viewport stuff:

   // Render (stolen from GameRenderWorld)
   PROFILE_START(GameRenderCommanderWorld);
   FrameAllocator::setWaterMark(0);

#if defined(GATHER_METRICS) && GATHER_METRICS > 1
   TextureManager::smTextureCacheMisses = 0;
#endif

   glEnable(GL_DEPTH_TEST);
   glDepthFunc(GL_LEQUAL);
   glClear(GL_DEPTH_BUFFER_BIT);
   glDisable(GL_CULL_FACE);
   glMatrixMode(GL_MODELVIEW);

   dglSetCanonicalState();
   // If you want to render other things, change this mask.
   gClientSceneGraph->renderScene(    EnvironmentObjectType | TerrainObjectType | InteriorObjectType | WaterObjectType );
   glDisable(GL_DEPTH_TEST);

#if defined(GATHER_METRICS) && GATHER_METRICS > 1
   Con::setFloatVariable("Video::texResidentPercentage",
                         TextureManager::getResidentFraction());
   Con::setIntVariable("Video::textureCacheMisses",
                       TextureManager::smTextureCacheMisses);
#endif

   AssertFatal(FrameAllocator::getWaterMark() == 0, "Error, someone didn't reset the water mark on the frame allocator!");
   FrameAllocator::setWaterMark(0);
   PROFILE_END();


   // Restore state
   gClientSceneGraph->setVisibleDistance(oldVisDist);
   gClientSceneGraph->setFogDistance    (oldFogDist);
   TerrainRender::mRenderingCommander = false;

   dglSetClipRect(updateRect);
}

void GuiCommanderHud::onRender(Point2I offset, const RectI &updateRect)
{
   // Update pan/zoom
   S32 time = Platform::getVirtualMilliseconds();
   S32 dt = time - mLastRenderTime;
   mLastRenderTime = time;

   mCurPan  += (mPanGoal  - mCurPan)  * (F32)dt/1000.f;
   mCurZoom += (mZoomGoal - mCurZoom) * (F32)dt/1000.f;

   // Render the world...
   Parent::onRender(offset, updateRect);

   // If you wanted to render custom GUI elements, like a sensor map, icons for
   // players/vehicles/objectives, you would do it here by calling project()
   // for all their positions and drawing bitmaps at the appropriate locations.
}

ConsoleMethod(GuiCommanderHud, pan, void, 4, 4, "(x, y) Cut to a location.")
{
   object->mPanGoal.set(dAtof(argv[2]), dAtof(argv[3]));
   object->mCurPan.set (dAtof(argv[2]), dAtof(argv[3]));
}

ConsoleMethod(GuiCommanderHud, panTo, void, 4, 4, "(x, y) Smoothly pan to a location.")
{
   object->mPanGoal.set(dAtof(argv[2]), dAtof(argv[3]));
}

ConsoleMethod(GuiCommanderHud, zoom, void, 3, 3, "(val) Zoom to a specified level.")
{
   object->mZoomGoal = dAtof(argv[2]);
   object->mCurZoom  = dAtof(argv[2]);
}

ConsoleMethod(GuiCommanderHud, zoomTo, void, 3, 3, "(val) Smoothly zoom to a specified level.")
{
   object->mZoomGoal = dAtof(argv[2]);
}

ConsoleMethod(GuiCommanderHud, zoomToArea, void, 6, 7, "(top, left, right, bottom, bool cut) Smoothly zoom to view the specified area. If cut is set, we jump there.")
{
   // Parse arguments
   F32 top, left, right, bottom;

   top    = dAtof(argv[2]);
   left   = dAtof(argv[3]);
   right  = dAtof(argv[4]);
   bottom = dAtof(argv[5]);

   // Figure out the center of the area
   Point2F center;

   center.x = (left + right) * 0.5f;
   center.y = (top + bottom) * 0.5f;

   object->mZoomGoal = mFabs(left - right) / 200; // Cheesy scaling fakery.

   // And set our motion
   object->mPanGoal = center;

   // Cut if requested
   if(argc > 6)
      if(dAtob(argv[6]))
      {
         object->mCurPan  = object->mPanGoal;
         object->mCurZoom = object->mZoomGoal;
      }
}